#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "CL/cl.h"
#define MD5 0
#define SHA1 1

unsigned long int openCl(int length,unsigned char* hash,unsigned long int depart,int* masque,int algo )
{
         //Initialisation des paramètres 
        int err;
        size_t global;
	size_t local;
	cl_uint arg_index=0;
        cl_uint ret_num_devices;
        cl_device_id* device_id;           // compute device id 
        cl_context context;               // compute context
    	cl_command_queue* commande;      // compute command queue
        cl_program programme;           // compute program
        cl_kernel kernel;              // compute kernel
        cl_platform_id platform;
        cl_uint platNum;
	int lent_algo;
	if(algo == 0){
		lent_algo=16;
		printf("MD5\n");
	}else{
		lent_algo=20;
		printf("SHA1\n");
	}
        //platform
        err == clGetPlatformIDs(1, &platform, &platNum);
        int gpu = 0;
        if(platNum == 0){
                printf("No platform available\n");
                exit(1);
        }

        //device
        size_t len,MAX_WORK_GROUP_SIZE;
        char buffer[2048];
        cl_uint MAX_COMPUTE_UNITS[10],CONTEXT_NUM_DEVICES[10];
        err = clGetDeviceIDs(platform,CL_DEVICE_TYPE_CPU,  0, NULL, &ret_num_devices);
        if (err != CL_SUCCESS)
        {
                 printf("Error: Failed to get number of device ID\n");
                exit(1);
        }
        device_id = (cl_device_id*)malloc(sizeof(cl_device_id)*ret_num_devices);
        err = clGetDeviceIDs(platform,CL_DEVICE_TYPE_CPU,  ret_num_devices, device_id,NULL );
        if (err != CL_SUCCESS)
        {
                printf("Error: Failed to get device ID\n");
                exit(1);
        }
        int i=0;
        for(i=0;i<ret_num_devices;i++){
                err = clGetDeviceInfo(device_id[i], CL_DEVICE_NAME, sizeof(buffer), buffer, &len);
                printf("CL_DEVICE_NAME: %s \n", buffer);
                err = clGetDeviceInfo(device_id[i], CL_DEVICE_VENDOR, sizeof(buffer), buffer, &len);
                printf("CL_DEVICE_VENDOR: %s\n", buffer);
                err = clGetDeviceInfo(device_id[i], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(MAX_COMPUTE_UNITS), MAX_COMPUTE_UNITS, &len);
                printf("CL_DEVICE_MAX_COMPUTE_UNIT: %i\n", *MAX_COMPUTE_UNITS);
                err = clGetDeviceInfo(device_id[i], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(MAX_WORK_GROUP_SIZE), &MAX_WORK_GROUP_SIZE, &len);
                printf("CL_DEVICE_MAX_WORK_GROUP_SIZE: %i\n", MAX_WORK_GROUP_SIZE);
        }

        context = clCreateContext(NULL,ret_num_devices,device_id,NULL,NULL,&err);
        if (!context)
        {
                if(err==CL_INVALID_PLATFORM) 
			printf("1");
		else if(err==CL_INVALID_VALUE ) 
			printf("2");
		else if(err==CL_INVALID_DEVICE ) 
			printf("3");
		else if(err==CL_DEVICE_NOT_AVAILABLE) 
			printf("4"); 
		else if(err==CL_OUT_OF_HOST_MEMORY ) 
			printf("5");
		else 
			printf("6");                
		return EXIT_FAILURE;
        }
        err = clGetContextInfo(context, CL_CONTEXT_NUM_DEVICES, sizeof(CONTEXT_NUM_DEVICES), CONTEXT_NUM_DEVICES, &len);
        printf("CL_CONTEXT_DEVICE_NUMBER: %i\n", *CONTEXT_NUM_DEVICES);
        commande = (cl_command_queue*)malloc(sizeof(cl_command_queue)*ret_num_devices);
        for(i=0;i<ret_num_devices;i++){
                commande[i] = clCreateCommandQueue(context, device_id[i], CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, &err);
                if (!commande[i])
                {
                        printf("Error: Failed to create a command!\n");
                        return EXIT_FAILURE;
                }
        }

        FILE* fp;
        char* source_prog;
        if((fp = fopen("MD5True.cl", "r" )) == NULL) {
                fprintf(stderr, "Erreur, impossible d'ouvrir le fichier en lecture\n" );
                exit(2);
        }
        i=0;
        char c;
        while((c = fgetc(fp)) != EOF) {
                i++;
        }
        i++;
        fclose(fp);
        fp = fopen("MD5True.cl", "r" );
        printf("taille: %i \n", i);
        source_prog = (char*)malloc(sizeof(char)*i);
        fread(source_prog , 1, i, fp);
        size_t longueur[1];

        longueur[0]=strlen(source_prog);
        programme = clCreateProgramWithSource(context, 1, (const char **) &source_prog, NULL, &err);
        if (err!=CL_SUCCESS)
        {
                printf("Error: Failed to create compute program!\n");
                return EXIT_FAILURE;
        }

	char option[50];
	strcpy(option," ");


        err = clBuildProgram(programme, ret_num_devices, device_id, option, NULL, NULL);
        if (err != CL_SUCCESS)
        {
		char coucou[9000];
		size_t ret=0;
		clGetProgramBuildInfo(programme,device_id[0],CL_PROGRAM_BUILD_LOG,9000,coucou,&ret);
                printf("Error: Failed to build program executable!\n%i\n%i\n%s\n",err,ret,coucou);
                exit(1);
        }

        kernel = clCreateKernel(programme, "hashage", &err);
        if (err != CL_SUCCESS)
        {
                printf("Error: Failed to create compute kernel!\n");

                exit(1);
        }


        cl_mem rate;
        cl_mem mask;
        cl_mem hache;

        const size_t local_ws = MAX_WORK_GROUP_SIZE;    // Number of work-items per work-group

        const size_t global_ws = local_ws*length;      // Total number of work-items
        printf("local: %u \nglobal: %u\n",local_ws,global_ws);
        rate = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(unsigned long int), NULL, &err);
        hache = clCreateBuffer(context, CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR, sizeof(unsigned char)*lent_algo, hash, &err);
        mask = clCreateBuffer(context, CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR, sizeof(int)*15, masque, &err);
        if (err!=CL_SUCCESS)
        {
                 printf("Error: Failed to create buffer!%i %i\n",err,CL_INVALID_HOST_PTR);
                 exit(1);
        }

	arg_index=0;
        err = 0;
        err  = clSetKernelArg(kernel, arg_index, sizeof(unsigned long int), &depart);
	arg_index++;
        err  = clSetKernelArg(kernel, arg_index, sizeof(int), &algo);
	arg_index++;
        err |= clSetKernelArg(kernel, arg_index, sizeof(cl_mem), &rate);
	arg_index++;
        err |= clSetKernelArg(kernel, arg_index, sizeof(cl_mem), &hache);
	arg_index++;
        err |= clSetKernelArg(kernel, arg_index, sizeof(cl_mem), &mask);
	arg_index++;
        if (err != CL_SUCCESS)
        {
			printf("Error: failed to set argument!\n");
                 exit(1);
        }
	unsigned long int C[1]={0};

        for(i=0;i<ret_num_devices;i++){
                err = clEnqueueWriteBuffer(commande[i], rate, CL_TRUE, 0, sizeof(unsigned long int), C, 0, NULL, NULL);
                err = clEnqueueWriteBuffer(commande[i], hache, CL_TRUE, 0, sizeof(unsigned char)*lent_algo, hash, 0, NULL, NULL);
                err = clEnqueueWriteBuffer(commande[i], mask, CL_TRUE, 0, sizeof(unsigned char)*15, masque, 0, NULL, NULL);
                if (err != CL_SUCCESS)
                {
                        printf("Error: Failed to write buffer!\n");
                        exit(1);
                }
        }

	const size_t global_work_offsets[2][1]={{(global_ws/ret_num_devices)*0},{(global_ws/ret_num_devices)*1}};
        for(i=0;i<ret_num_devices;i++){
		err = clEnqueueNDRangeKernel(commande[i], kernel, 1,global_work_offsets[i], &global_ws, &local_ws, 0, NULL, NULL);
             if (err != CL_SUCCESS)
                {
                        printf("Error: Failed to ND range enqueue!\n");
                        exit(1);
                }
	}
	for(i=0;i<ret_num_devices;i++){
		err=clFinish(commande[0]);
        	if (err != CL_SUCCESS) {
                	printf("\n Error number %d", err);
        	}
	}

        for(i=0;i<ret_num_devices;i++){
   		err = clEnqueueReadBuffer(commande[i], rate, CL_TRUE, 0, sizeof(unsigned long int), C, 0, NULL, NULL);
		if (err != CL_SUCCESS)
                {
                        printf("Error: Failed to recup donnée! %i\n",err);
                        exit(1);
                }

	}   
//	printf("finish \n");

	clReleaseMemObject(hache);
  	clReleaseMemObject(rate);
  	clReleaseMemObject(mask);
  	clReleaseProgram(programme);
  	clReleaseKernel(kernel);
//	clReleaseCommandQueue(commande);
  	clReleaseContext(context);
	return C[0];
}

/*MD5*/
typedef unsigned char *POINTER;

typedef unsigned short int UINT2;

typedef unsigned int UINT4;

#define PROTO_LIST(list) ()

typedef struct {
  UINT4 state[4];
  UINT4 count[2];
  unsigned char buffer[64];
} MD5_CTX;

void MD5Init (MD5_CTX *);
void MD5Update (MD5_CTX *, unsigned char *,unsigned int);
void MD5Final (unsigned char [16], MD5_CTX *);

#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21

void MD5Transform  (UINT4 state[4], unsigned char block[64]);
void Encode (unsigned char *, UINT4 *, unsigned int);
void Decode (UINT4 *, unsigned char *,unsigned int);
void MD5_memcpy (POINTER, POINTER, unsigned int);
void MD5_memset (POINTER, int, unsigned int);

#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))

#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))

#define FF(a, b, c, d, x, s, ac) { a += F (b, c, d) + x + (UINT4)ac; a = ROTATE_LEFT (a, s); a += b;}
#define GG(a, b, c, d, x, s, ac) { a += G (b, c, d) + x + (UINT4)ac; a = ROTATE_LEFT (a, s); a += b;}
#define HH(a, b, c, d, x, s, ac) { a += H (b, c, d) + x + (UINT4)ac; a = ROTATE_LEFT (a, s); a += b;}
#define II(a, b, c, d, x, s, ac) { a += I (b, c, d) + x + (UINT4)ac; a = ROTATE_LEFT (a, s); a += b;}
void MD5Init (MD5_CTX *context)
{
  context->count[0] = context->count[1] = 0;

  context->state[0] = 0x67452301;
  context->state[1] = 0xefcdab89;
  context->state[2] = 0x98badcfe;
  context->state[3] = 0x10325476;
}

void MD5Update (MD5_CTX *context,unsigned char *input,unsigned int inputLen)
{
  unsigned int i, index, partLen;

  index = (unsigned int)((context->count[0] >> 3) & 0x3F);

  if ((context->count[0] += ((UINT4)inputLen << 3))

   < ((UINT4)inputLen << 3))
 context->count[1]++;
  context->count[1] += ((UINT4)inputLen >> 29);
  partLen = 64 - index;

  if (inputLen >= partLen) {
  MD5_memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
  MD5Transform (context->state, context->buffer);

 for (i = partLen; i + 63 < inputLen; i += 64)
   MD5Transform (context->state, &input[i]);

 index = 0;
  }
  else
 i = 0;

  MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i],inputLen-i);
}

void MD5Final (unsigned char digest[16],MD5_CTX *context)
{
unsigned char PADDING[64] = {
  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

  unsigned char bits[8];
 unsigned int index, padLen;

  Encode (bits, context->count, 8);

  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
  padLen = (index < 56) ? (56 - index) : (120 - index);
  MD5Update (context, PADDING, padLen);

  MD5Update (context, bits, 8);

  Encode (digest, context->state, 16);

  MD5_memset ((POINTER)context, 0, sizeof (*context));
}

void MD5Transform (UINT4 state[4],unsigned char block[64])
{
  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];

  Decode (x, block, 64);

  FF (a, b, c, d, x[ 0], S11, 0xd76aa478);
  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756);
  FF (c, d, a, b, x[ 2], S13, 0x242070db);
  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee);
  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf);
  FF (d, a, b, c, x[ 5], S12, 0x4787c62a);
  FF (c, d, a, b, x[ 6], S13, 0xa8304613);
  FF (b, c, d, a, x[ 7], S14, 0xfd469501);
  FF (a, b, c, d, x[ 8], S11, 0x698098d8);
  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af);
  FF (c, d, a, b, x[10], S13, 0xffff5bb1);
  FF (b, c, d, a, x[11], S14, 0x895cd7be);
  FF (a, b, c, d, x[12], S11, 0x6b901122);
  FF (d, a, b, c, x[13], S12, 0xfd987193);
  FF (c, d, a, b, x[14], S13, 0xa679438e);
  FF (b, c, d, a, x[15], S14, 0x49b40821);

  GG (a, b, c, d, x[ 1], S21, 0xf61e2562);
  GG (d, a, b, c, x[ 6], S22, 0xc040b340);
  GG (c, d, a, b, x[11], S23, 0x265e5a51);
  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa);
  GG (a, b, c, d, x[ 5], S21, 0xd62f105d);
  GG (d, a, b, c, x[10], S22,  0x2441453);
  GG (c, d, a, b, x[15], S23, 0xd8a1e681);
  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8);
  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6);
  GG (d, a, b, c, x[14], S22, 0xc33707d6);
  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87);

  GG (b, c, d, a, x[ 8], S24, 0x455a14ed);
  GG (a, b, c, d, x[13], S21, 0xa9e3e905);
  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8);
  GG (c, d, a, b, x[ 7], S23, 0x676f02d9);
  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a);

  HH (a, b, c, d, x[ 5], S31, 0xfffa3942);
  HH (d, a, b, c, x[ 8], S32, 0x8771f681);
  HH (c, d, a, b, x[11], S33, 0x6d9d6122);
  HH (b, c, d, a, x[14], S34, 0xfde5380c);
  HH (a, b, c, d, x[ 1], S31, 0xa4beea44);
  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9);
  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60);
  HH (b, c, d, a, x[10], S34, 0xbebfbc70);
  HH (a, b, c, d, x[13], S31, 0x289b7ec6);
  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa);
  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085);
  HH (b, c, d, a, x[ 6], S34,  0x4881d05);
  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039);
  HH (d, a, b, c, x[12], S32, 0xe6db99e5);
  HH (c, d, a, b, x[15], S33, 0x1fa27cf8);
  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665);

II (a, b, c, d, x[ 0], S41, 0xf4292244);
  II (d, a, b, c, x[ 7], S42, 0x432aff97);
  II (c, d, a, b, x[14], S43, 0xab9423a7);
  II (b, c, d, a, x[ 5], S44, 0xfc93a039);
  II (a, b, c, d, x[12], S41, 0x655b59c3);
  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92);
  II (c, d, a, b, x[10], S43, 0xffeff47d);
  II (b, c, d, a, x[ 1], S44, 0x85845dd1);
  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f);
  II (d, a, b, c, x[15], S42, 0xfe2ce6e0);
  II (c, d, a, b, x[ 6], S43, 0xa3014314);
  II (b, c, d, a, x[13], S44, 0x4e0811a1);
  II (a, b, c, d, x[ 4], S41, 0xf7537e82);
  II (d, a, b, c, x[11], S42, 0xbd3af235);
  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb);
  II (b, c, d, a, x[ 9], S44, 0xeb86d391);

  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;

  MD5_memset ((POINTER)x, 0, sizeof (x));
}
void Encode (unsigned char *output,UINT4 *input,unsigned int len)
{
 unsigned int i, j;

  for (i = 0, j = 0; j < len; i++, j += 4) {
 output[j] = (unsigned char)(input[i] & 0xff);
 output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
 output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
 output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
  }
}

void Decode (UINT4 *output,unsigned char *input,unsigned int len)
{
  unsigned int i, j;

  for (i = 0, j = 0; j < len; i++, j += 4)
 output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
   (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
}
void MD5_memcpy (POINTER output,POINTER input,unsigned int len)
{
  unsigned int i;

  for (i = 0; i < len; i++)
         output[i] = input[i];
}

void MD5_memset (POINTER output,int value,unsigned int len)
{
  unsigned int i;

  for (i = 0; i < len; i++)
 ((char *)output)[i] = (char)value;
}
/*MD5*/

/*SHA1*/
enum
{
    shaSuccess = 0,
    shaNull,            /* Null pointer parameter */
    shaInputTooLong,    /* input data too long */
    shaStateError       /* called Input after Result */
};
#define SHA1HashSize 20
typedef struct SHA1Context
{
    unsigned int Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */

    unsigned int Length_Low;            /* Message length in bits      */
    unsigned int Length_High;           /* Message length in bits      */

                               /* Index into message block array   */
    unsigned short int Message_Block_Index;
    unsigned char Message_Block[64];      /* 512-bit message blocks      */

    int Computed;               /* Is the digest computed?         */
    int Corrupted;             /* Is the message digest corrupted? */
} SHA1Context;
int SHA1Reset( SHA1Context *);
int SHA1Input( SHA1Context *, unsigned char *,unsigned int);
int SHA1Result( SHA1Context *,unsigned char Message_Digest[SHA1HashSize]);
#define SHA1CircularShift(bits,word) (((word) << (bits)) | ((word) >> (32-(bits))))

void SHA1PadMessage(SHA1Context *);
void SHA1ProcessMessageBlock(SHA1Context *);

int SHA1Reset(SHA1Context *context)
{
    if (!context)
    {
        return shaNull;
    }

    context->Length_Low             = 0;
    context->Length_High            = 0;
    context->Message_Block_Index    = 0;
 context->Intermediate_Hash[0]   = 0x67452301;
    context->Intermediate_Hash[1]   = 0xEFCDAB89;
    context->Intermediate_Hash[2]   = 0x98BADCFE;
    context->Intermediate_Hash[3]   = 0x10325476;
    context->Intermediate_Hash[4]   = 0xC3D2E1F0;

    context->Computed   = 0;
    context->Corrupted  = 0;

    return shaSuccess;
}
int SHA1Result( SHA1Context *context,unsigned char Message_Digest[SHA1HashSize])
{
    int i;

    if (!context || !Message_Digest)
    {
        return shaNull;
    }

    if (context->Corrupted)
    {
        return context->Corrupted;
    }
 if (!context->Computed)
    {
        SHA1PadMessage(context);
        for(i=0; i<64; ++i)
        {
            context->Message_Block[i] = 0;
        }
        context->Length_Low = 0;    /* and clear length */
        context->Length_High = 0;
        context->Computed = 1;

    }

    for(i = 0; i < SHA1HashSize; ++i){
        Message_Digest[i] = context->Intermediate_Hash[i>>2]
                            >> 8 * ( 3 - ( i & 0x03 ) );
    }
    return shaSuccess;
}
int SHA1Input(SHA1Context* context, unsigned char* message_array, unsigned int length)
{
    if (!length)
    {
        return shaSuccess;
    }

    if (!context || !message_array){
        return shaNull;
    }

    if (context->Computed){
        context->Corrupted = shaStateError;
        return shaStateError;
    }

    if (context->Corrupted){
         return context->Corrupted;
    }
    while(length-- && !context->Corrupted)
    {
    context->Message_Block[context->Message_Block_Index++] =
                   (*message_array & 0xFF);

    context->Length_Low += 8;
    if (context->Length_Low == 0){
        context->Length_High++;
        if (context->Length_High == 0) {
            context->Corrupted = 1;
        }
    }
    if (context->Message_Block_Index == 64){
        SHA1ProcessMessageBlock(context);
    }
    message_array++;
    }
    return shaSuccess;
}
void SHA1ProcessMessageBlock(SHA1Context *context)
{
    const unsigned int K[] =    {       /* Constants defined in SHA-1   */
                            0x5A827999,
                            0x6ED9EBA1,
                            0x8F1BBCDC,
                            0xCA62C1D6
                            };
    int           t;                 /* Loop counter                */
    unsigned int      temp;              /* Temporary word value        */
    unsigned int      W[80];             /* Word sequence               */
    unsigned int      A, B, C, D, E;     /* Word buffers                */

    /*
     *  Initialize the first 16 words in the array W
     */
    for(t = 0; t < 16; t++)
    {
        W[t] = context->Message_Block[t * 4] << 24;
        W[t] |= context->Message_Block[t * 4 + 1] << 16;
        W[t] |= context->Message_Block[t * 4 + 2] << 8;
        W[t] |= context->Message_Block[t * 4 + 3];
    }

  for(t = 16; t < 80; t++)
    {
       W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
    }

    A = context->Intermediate_Hash[0];
    B = context->Intermediate_Hash[1];
    C = context->Intermediate_Hash[2];
    D = context->Intermediate_Hash[3];
    E = context->Intermediate_Hash[4];

    for(t = 0; t < 20; t++)
    {
        temp =  SHA1CircularShift(5,A) +
                ((B & C) | ((~B) & D)) + E + W[t] + K[0];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);

        B = A;
        A = temp;
    }
    for(t = 20; t < 40; t++)
    {
        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }

    for(t = 40; t < 60; t++)
    {
        temp = SHA1CircularShift(5,A) +
               ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }

   for(t = 60; t < 80; t++)
    {
        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }

    context->Intermediate_Hash[0] += A;
    context->Intermediate_Hash[1] += B;
    context->Intermediate_Hash[2] += C;
    context->Intermediate_Hash[3] += D;
    context->Intermediate_Hash[4] += E;

    context->Message_Block_Index = 0;
}
void SHA1PadMessage(SHA1Context *context)
{
    if (context->Message_Block_Index > 55)
    {
        context->Message_Block[context->Message_Block_Index++] = 0x80;
        while(context->Message_Block_Index < 64)
        {
            context->Message_Block[context->Message_Block_Index++] = 0;
        }

        SHA1ProcessMessageBlock(context);

        while(context->Message_Block_Index < 56)
        {
            context->Message_Block[context->Message_Block_Index++] = 0;
        }
    }
    else
    {
        context->Message_Block[context->Message_Block_Index++] = 0x80;
        while(context->Message_Block_Index < 56)
        {
            context->Message_Block[context->Message_Block_Index++] = 0;
        }
    }
    context->Message_Block[56] = context->Length_High >> 24;
    context->Message_Block[57] = context->Length_High >> 16;
    context->Message_Block[58] = context->Length_High >> 8;
    context->Message_Block[59] = context->Length_High;
    context->Message_Block[60] = context->Length_Low >> 24;
    context->Message_Block[61] = context->Length_Low >> 16;
    context->Message_Block[62] = context->Length_Low >> 8;
    context->Message_Block[63] = context->Length_Low;

    SHA1ProcessMessageBlock(context);
}
/*SHA1*/



void createMask(int*mask,char*mot){
	int i,len = strlen(mot);
	for(i=0;i<len;i++){
		if(mot[i]<122 && mot[i]>96)
			mask[i]=0;
		else if(mot[i]<91 && mot[i]>64)
			mask[i]=1;
		else if(mot[i]<58 && mot[i]>47)
			mask[i]=2;
		else 
			mask[i]=3;	

	}
	for(i=14;i>=len;i--)
		mask[i]=-1;
}
int main (int argv, char* argc[] ){
	if(argv < 5){
		printf("utilisation:\n");
		printf("./try \"mot_a_chercher\" nombre_de_mot_a_tester indice_premier_mot, algo_de_hash\n");
		printf("exemple MD5 ./try beckie 100000 0 0\n");
		printf("exemple SHA1 ./try beckie 100000 0 1\n");
		exit(0);
	}
	int j;
	char* mot=argc[1];
	printf("%s\n",mot);

	int mask[15];
	createMask(mask,mot);
	for(j=0;j<15;j++)
	printf("%i",mask[j]);
		
//      int mask[15]={0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1};
//	unsigned char hash[20]={0x1c,0x56,0x51,0xf0,0x9e,0x75,0xed,0xf8,0xa2,0x80,0x38,0x9f,0x93,0x75,0x3e,0x45,0x15,0x27,0x26,0xa2};
		unsigned char hash[20];

	if(atoi(argc[4])==0){
	        MD5_CTX context;
	        MD5Init (&context);
	        MD5Update (&context,mot, strlen(mot));
	        MD5Final (hash, &context);
        }else{        
		SHA1Context sha;
                SHA1Reset(&sha);
                SHA1Input(&sha, mot,strlen(mot));
                SHA1Result(&sha, hash);
	}

//	unsigned char hash[16]={0x3d,0x56,0x3d,0xe1,0xeb,0x08,0x9b,0xbe,0x8f,0xd1,0xaf,0x6c,0x92,0xd5,0x0c,0xd3};
	unsigned int i = openCl(atoi(argc[2]),hash,atoi(argc[3]),mask ,atoi(argc[4]));
	printf("%u\n",i);
}

